/*
 * Copyright 2012 Faculty of Informatics - Masaryk University.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cz.muni.fi.pa165.creatures.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

/**
 * This class represent the type of a {@link Creature}.
 *
 * @author smikloso
 */
@Entity
@Table(uniqueConstraints = {
    @UniqueConstraint(columnNames = "NAME")
})
public class CreatureType implements Serializable {

    private static final long serialVersionUID = 7134213085209774823L;

    /**
     * Id of the creature type.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "ID", nullable = false)
    private Long id;

    /**
     * Name of the creature type. This attribute has to be set.
     */
    @Column(name = "NAME", nullable = false, length = 255)
    private String name;

    /**
     * Description of the creature type.
     */
    @Column(name = "DESCRIPTION", length = 512)
    private String description;

    /**
     * Maximum hit points of the creature of some type. More hit points a
     * creature has, more difficult it is to kill it by some {@link Weapon}.
     * This attribute has to be set.
     *
     * Effectiveness of some {@link Weapon} against particular
     * {@link CreatureType} is described by
     * {@link CreatureTypeWeapon#effectivity}.
     */
    @Column(name = "MAX_HIT_POINTS", nullable = false)
    private Integer maxHitPoints;

    /**
     * Typical number of the creatures of some type in the group while moving.
     */
    @Column(name = "CREATURES_IN_GROUP")
    private Integer creaturesInGroup;

    /**
     * Typical strategy of some type of creature when it is under attack.
     */
    @Column(name = "DEFENSE_STRATEGY")
    @Enumerated(EnumType.STRING)
    private DefenseStrategy defenseStrategy;

    @OneToMany(mappedBy = "creatureType", cascade = {CascadeType.REMOVE})
    private List<Creature> creatures = new ArrayList<Creature>();

    @OneToMany(mappedBy = "creatureType", fetch = FetchType.EAGER)
    private List<CreatureTypeWeapon> weapons;

    public CreatureType() {
    }

    /**
     * @param name name of the creature type
     * @param maxHitPoints maximum hit points of the creature type
     */
    public CreatureType(String name, Integer maxHitPoints) {
        this.name = name;
        this.maxHitPoints = maxHitPoints;
    }

    public CreatureType(String name, String description, Integer maxHitPoints,
            Integer creaturesInGroup, DefenseStrategy defenseStrategy) {
        this.name = name;
        this.description = description;
        this.maxHitPoints = maxHitPoints;
        this.creaturesInGroup = creaturesInGroup;
        this.defenseStrategy = defenseStrategy;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    /**
     * @param name name of a creature, may not be longer than 255 characters and
     * has to be present.
     */
    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    /**
     * @param description description of the creature, typically arbitrary text
     * of the length at most of 512 characters.
     */
    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getMaxHitPoints() {
        return maxHitPoints;
    }

    /**
     * Sets maximum hit points of a creature type.
     *
     * Hint - imagine an average human has 100 hit points. If you set health to
     * 200 hit points, that creature should be twice as hard to kill as human.
     * This attribute has to be set.
     *
     * @param maxHitPoints maximum hit points of the creature
     */
    public void setMaxHitPoints(Integer maxHitPoints) {
        this.maxHitPoints = maxHitPoints;
    }

    public Integer getCreaturesInGroup() {
        return creaturesInGroup;
    }

    public List<Creature> getCreatures() {
        return new ArrayList<Creature>(creatures);
    }

    /**
     * This method removes a creature from the list of creatures of this
     * creature type, if a creature is of this creature type.
     *
     * This method keeps relationship consistency.
     *
     * @param creature creature to remove from the list of creatures of this
     * type
     */
    public void removeCreature(Creature creature) {
        if (creature != null) {
            if (!creatures.contains(creature)) {
                return;
            }
            creatures.remove(creature);
            creature.setCreatureType(null);
        }
    }

    /**
     * This method adds a creature to the list of creatures of this creature
     * type, if the list does not already contain it.
     *
     * This method keeps relationship consistency. {@code this} creature type is
     * set as the creature type a {@code creature} is an instance of.
     *
     * @param creature creature to add to the region
     */
    public void addCreature(Creature creature) {
        if (creature != null) {
            if (creatures.contains(creature)) {
                return;
            }
            creatures.add(creature);
            creature.setCreatureType(this);
        }
    }

    public List<CreatureTypeWeapon> getWeapons() {
        return weapons;
    }

    public void setWeapons(List<CreatureTypeWeapon> weapons) {
        this.weapons = weapons;
    }

    /**
     * Sets typical number of creatures while moving in a group.
     *
     * @param creaturesInGroup typical number of creatures while moving in a
     * group of this {@code CreatureType}.
     */
    public void setCreaturesInGroup(Integer creaturesInGroup) {
        this.creaturesInGroup = creaturesInGroup;
    }

    public DefenseStrategy getDefenseStrategy() {
        return defenseStrategy;
    }

    public void setDefenseStrategy(DefenseStrategy defenseStrategy) {
        this.defenseStrategy = defenseStrategy;
    }

    @Override
    public String toString() {
        return "CreatureType{" + "id= " + id
                + ", name= " + name
                + ", description= " + description
                + ", strategy= " + defenseStrategy.toString()
                + ", max hit points= " + maxHitPoints
                + ", creaturesInGroup= " + creaturesInGroup
                + '}';
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 83 * hash + (this.id != null ? this.id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final CreatureType other = (CreatureType) obj;
        if (this.id != other.id && (this.id == null || !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }
}
